perm filename TCPPRO.C[IP,SYS] blob
sn#680203 filedate 1982-10-07 generic text, type T, neo UTF8
#include "../h/param.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"
/*
* TCP finite state machine procedures.
*
* Called from finite state machine action routines, these do most of the work
* of the protocol. They in turn call primitive routines (in tcp_prim) to
* perform lower level functions.
*/
/*
* Set up a TCB for a connection
*/
t_open(tp, mode)
register struct tcb *tp;
int mode;
{
register struct ucb *up = tp->t_ucb;
/* enqueue the tcb */
if (netcb.n_tcb_head == NULL) {
netcb.n_tcb_head = tp;
netcb.n_tcb_tail = tp;
} else {
tp->t_tcb_next = netcb.n_tcb_head;
netcb.n_tcb_head->t_tcb_prev = tp;
netcb.n_tcb_head = tp;
}
/* initialize non-zero tcb fields */
tp->t_rcv_next = (struct th *)tp;
tp->t_rcv_prev = (struct th *)tp;
tp->t_xmtime = T_REXMT;
#ifndef NOTCPOPTS
tp->t_maxseg = MIN(up->uc_srcif->if_mtu - TCPIPMAX, TCPMAXSND);
#else
tp->t_maxseg = up->uc_srcif->if_mtu - TCPIPMAX;
#endif
tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi =
tp->snd_una = tp->iss = netcb.n_iss;
netcb.n_iss += (ISSINCR >> 1) + 1;
/* set timeout for open */
tp->t_timers[TINIT] = (up->uc_timeo != 0 ? up->uc_timeo :
(mode == TCP_ACTIVE ? T_INIT : 0));
up->uc_timeo = 0; /* overlays uc_ssize */
}
/*
* Delete TCB and free all resources used by the connection. Called after
* the close protocol is complete.
*/
t_close(tp, state)
register struct tcb *tp;
short state;
{
register struct ucb *up;
register struct th *t;
register struct mbuf *m;
register struct work *w;
int i;
up = tp->t_ucb;
/* cancel all timers */
for (i = TINIT; i <= TFINACK; i++)
tp->t_timers[i] = 0;
/* remove all work entries for tcb */
for (w = netcb.n_work; w != NULL; w = w->w_next)
if (w->w_tcb == tp)
w->w_type = INOP;
/* delete tcb */
if (tp->t_tcb_prev == NULL)
netcb.n_tcb_head = tp->t_tcb_next;
else
tp->t_tcb_prev->t_tcb_next = tp->t_tcb_next;
if (tp->t_tcb_next == NULL)
netcb.n_tcb_tail = tp->t_tcb_prev;
else
tp->t_tcb_next->t_tcb_prev = tp->t_tcb_prev;
/* free all data on receive and send buffers */
for (t = tp->t_rcv_next; t != (struct th *)tp; t = t->t_next)
m_freem(dtom(t));
if (up->uc_rbuf != NULL) {
m_freem(up->uc_rbuf);
up->uc_rbuf = NULL;
}
if (up->uc_sbuf != NULL) {
m_freem(up->uc_sbuf);
up->uc_sbuf = NULL;
}
for (m = tp->t_rcv_unack; m != NULL; m = m->m_act) {
m_freem(m);
tp->t_rcv_unack = NULL;
}
/* free tcb buffer */
m_free(dtom(tp));
up->uc_tcb = NULL;
/* lower buffer allocation and decrement host entry */
netcb.n_lowat -= up->uc_snd + up->uc_rcv + 2;
netcb.n_hiwat = (netcb.n_lowat * 3) >> 1;
if (up->uc_route != NULL) {
h_free(up->uc_route);
up->uc_route = NULL;
}
/* if user has initiated close (via close call), delete ucb
entry, otherwise just wakeup so user can issue close call */
if (tp->usr_abort) {
if (up->uc_flags & UCWAIT)
wakeup(tp);
up->uc_proc = NULL;
if (up->uc_next != NULL)
up->uc_next->uc_prev = up->uc_prev;
if (up->uc_prev != NULL)
up->uc_prev->uc_next = up->uc_next;
else
netcb.n_ucb_hd = up->uc_next;
m_free(dtom(up));
} else
to_user(up, state);
}
/*
* Send a TCP segment. Send data from left window edge of send buffer up to
* window size or end (whichever is less). Set retransmission timers.
*/
send_tcp(tp, ctl)
register struct tcb *tp;
int ctl;
{
register struct ucb *up;
register sequence last, wind;
struct mbuf *m;
int forced, sent;
int snd_flags;
struct mbuf *snd_copy();
int len;
up = tp->t_ucb;
snd_flags = 0;
tp->snd_lst = tp->snd_nxt;
forced = FALSE;
m = NULL;
/*
* Send SYN if this is first data (ISS)
*/
if (SEQ_EQ(tp->snd_nxt, tp->iss)) {
snd_flags |= T_SYN;
tp->snd_lst++;
}
/*
* Get seq # of last datum in send buffer
*/
last = tp->snd_una;
if (!tp->syn_acked)
last++; /* don't forget SYN */
for (m = up->uc_sbuf; m != NULL; m = m->m_next)
last += m->m_len;
/*
* If no data to send in buffer, just do FIN check, otherwise see
* how much we should send in segment.
*/
if (SEQ_GEQ(tp->snd_nxt, last)) {
/*
* should send FIN? don't unless haven't already sent one
*/
if (tp->snd_fin &&
(SEQ_EQ(tp->seq_fin, tp->iss) ||
SEQ_LEQ(tp->snd_nxt, tp->seq_fin))) {
snd_flags |= T_FIN;
tp->seq_fin = tp->snd_lst++;
}
} else if (tp->syn_acked) {
/*
* Only send a segment if there is something in the buffer,
* and a window has been received.
*/
wind = tp->snd_una + tp->snd_wnd;
/* Use window to limit send */
tp->snd_lst = SEQ_MIN(last, wind);
/* Make sure we don't do IP fragmentation or send
more than peer can handle */
if ((len = tp->snd_lst - tp->snd_nxt) > tp->t_maxseg)
tp->snd_lst -= len - tp->t_maxseg;
/* see if PUSH should be sent */
if (SEQ_GT(tp->snd_end, tp->iss) &&
SEQ_LEQ(tp->snd_end, tp->snd_lst))
snd_flags |= T_PUSH;
/* Avoid silly window syndrome: make sure usable window
is at least 25% of offered window. Be sure the user
is not blocked for send resources.
else if (!tp->rexmt && !tp->ack_due && !tp->snd_fin &&
up->uc_snd != up->uc_ssize && tp->snd_wnd > 0 &&
100*((tp->snd_lst-tp->snd_una)/tp->snd_wnd) < 25)
tp->snd_lst = tp->snd_nxt;
/* Set persist timer, if we exceeded the window */
if (SEQ_GEQ(tp->snd_lst, wind))
tp->t_timers[TPERSIST] = T_PERS;
/* Check if window is closed and must force a byte out
(persist timer went off) */
if (tp->force_one && SEQ_EQ(tp->snd_lst, wind)) {
tp->snd_lst = tp->snd_nxt + 1;
forced = TRUE;
}
/* copy data to send from send buffer */
m = snd_copy(tp, SEQ_MAX(tp->iss+1,tp->snd_nxt), tp->snd_lst);
/* must send FIN and no more data left to send after this */
if (tp->snd_fin && !forced && SEQ_EQ(tp->snd_lst, last) &&
(SEQ_EQ(tp->seq_fin, tp->iss) ||
SEQ_LEQ(tp->snd_nxt, tp->seq_fin))) {
snd_flags |= T_FIN;
tp->seq_fin = tp->snd_lst++;
}
}
/*
* If there is something to send, do it and update timers for rexmt.
*/
if (SEQ_LT(tp->snd_nxt, tp->snd_lst)) {
sent = send_pkt(tp, snd_flags,
(int)(tp->snd_lst-tp->snd_nxt), m);
/* set timers for retransmission if necessary */
if (!forced) {
tp->t_timers[TREXMT] = tp->t_xmtime;
tp->t_rexmt_val = tp->snd_lst;
if (!tp->rexmt) {
tp->t_timers[TREXMTTL] = T_REXMTTL;
tp->t_rtl_val = tp->snd_lst;
}
}
/* update seq for next send if this one got out */
if (sent)
tp->snd_nxt = tp->snd_lst;
/* if last timed message has been acked, start timing
this one */
if (tp->syn_acked && SEQ_GT(tp->snd_una, tp->t_xmt_val)) {
tp->t_timers[TXMT] = 0;
tp->t_xmt_val = tp->snd_lst;
}
tp->ack_due = FALSE;
tp->snd_hi = SEQ_MAX(tp->snd_nxt, tp->snd_hi);
tp->rexmt = FALSE;
tp->force_one = FALSE;
if (sent)
return(TRUE);
}
/*
* If ctl, make sure to send something so ACK gets through
*/
if (ctl == TCP_CTL) {
send_pkt(tp, 0, 0, NULL);
tp->ack_due = FALSE;
}
return(FALSE);
}
/*
* Process incoming segments
*/
rcv_tcp(tp, n, ctl)
register struct tcb *tp;
register struct th *n;
int ctl;
{
register struct mbuf *m;
register sequence last;
int sent;
tp->dropped_txt = FALSE;
tp->ack_due = FALSE;
tp->new_window = FALSE;
/*
* Process SYN
*/
if (!tp->syn_rcvd && n->t_flags&T_SYN) {
tp->irs = n->t_seq;
tp->rcv_nxt = n->t_seq + 1;
tp->snd_wl = tp->rcv_urp = tp->irs;
tp->syn_rcvd = TRUE;
tp->ack_due = TRUE;
}
/*
* Process ACK if data not already acked previously. (Take ACKed data
* off send queue, and reset rexmt timers).
*/
if (n->t_flags&T_ACK && tp->syn_rcvd && SEQ_GT(n->t_ackno, tp->snd_una))
rcv_ack(tp, n);
/*
* Check for new window.
*/
if (tp->syn_rcvd && SEQ_GEQ(n->t_seq, tp->snd_wl)) {
tp->snd_wl = n->t_seq;
tp->snd_wnd = n->t_win;
tp->new_window = TRUE;
t_cancel(tp, TPERSIST); /* cancel persist timer */
}
/*
* For data packets only (vs. ctl), process data and URG.
*/
if (ctl == TCP_DATA) {
if (n->t_len != 0)
rcv_text(tp, n); /* accept and sequence data */
/*
* Process URG if new urgent pointer received.
*/
if (n->t_flags&T_URG) {
last = n->t_urp + n->t_seq;
if (SEQ_LT(tp->rcv_nxt, last)) {
/* Tell user about start of urgent data only */
if (SEQ_LEQ(tp->rcv_urp, tp->rcv_nxt))
to_user(tp->t_ucb, UURGENT);
tp->rcv_urp = last;
}
}
/*
* Process PUSH, mark end of data chain.
*/
if (n->t_flags&T_PUSH && !tp->dropped_txt &&
tp->t_rcv_prev != (struct th *)tp) {
/* Find last mbuf on received data chain and mark */
m = dtom(tp->t_rcv_prev);
if (m != NULL) {
while (m->m_next != NULL)
m = m->m_next;
m->m_act = (struct mbuf *)(m->m_off+m->m_len-1);
}
}
}
/*
* Process FIN, check for duplicates and make sure all data is in.
*/
if (n->t_flags&T_FIN && !tp->dropped_txt) {
if (tp->fin_rcvd)
tp->ack_due = TRUE;
else {
/*
* Check if we really have FIN
* (rcv buf filled in, no drops)
*/
last = firstempty(tp);
if ((tp->t_rcv_prev == (struct th *)tp &&
SEQ_EQ(last, t_end(n)+1)) ||
SEQ_EQ(last, t_end(tp->t_rcv_prev))) {
tp->fin_rcvd = TRUE;
wakeup(tp->t_ucb);
}
/*
* If FIN, then set to ACK: incr rcv_nxt, since FIN
* occupies sequence space
*/
if (tp->fin_rcvd && SEQ_GEQ(tp->rcv_nxt, last)) {
tp->rcv_nxt = last + 1;
tp->ack_due = TRUE;
}
}
}
/*
* If ACK required or rcv window has changed, try to send something.
*/
sent = FALSE;
if (tp->ack_due)
sent = send_tcp(tp, TCP_CTL);
else if (tp->new_window)
sent = send_tcp(tp, TCP_DATA);
/*
* Set rexmt and round trip timers, if anything sent, and unacked
* data left in queue.
*/
if (!sent && SEQ_LT(tp->snd_una, tp->snd_nxt) && tp->cancelled) {
tp->t_timers[TREXMT] = tp->t_xmtime;
tp->t_timers[TREXMTTL] = T_REXMTTL;
tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst;
tp->cancelled = FALSE;
}
}
/*
* Process incoming ACKs. Remove data from send queue up to acknowledgement.
* Also handles round-trip timer for retransmissions and acknowledgement of
* SYN.
*/
rcv_ack(tp, n)
register struct tcb *tp;
register struct th *n;
{
register struct ucb *up;
register struct mbuf *m;
register len;
up = tp->t_ucb;
len = n->t_ackno - tp->snd_una;
tp->snd_una = n->t_ackno;
if (SEQ_GT(tp->snd_una, tp->snd_nxt))
tp->snd_nxt = tp->snd_una;
/* if timed message has been acknowledged, use the time to set
the retransmission time value */
if (tp->syn_acked && SEQ_GT(tp->snd_una, tp->t_xmt_val)) {
tp->t_xmtime = (tp->t_timers[TXMT]>T_REXMT ? tp->t_timers[TXMT]
: T_REXMT);
if (tp->t_xmtime > T_REMAX)
tp->t_xmtime = T_REMAX;
}
/* handle ack of opening syn (tell user) */
if (!tp->syn_acked && SEQ_GT(tp->snd_una, tp->iss)) {
tp->syn_acked = TRUE;
len--; /* ignore SYN */
t_cancel(tp, TINIT); /* cancel init timer */
}
/* remove acknowledged data from send buff */
m = up->uc_sbuf;
while (len > 0 && m != NULL)
if (m->m_len <= len) {
len -= m->m_len;
m = m_free(m);
up->uc_ssize--;
} else {
m->m_len -= len;
m->m_off += len;
break;
}
up->uc_sbuf = m;
wakeup(tp->t_ucb);
/* handle ack of closing fin */
if (SEQ_NEQ(tp->seq_fin, tp->iss) && SEQ_GT(tp->snd_una, tp->seq_fin))
tp->snd_fin = FALSE;
t_cancel(tp, TREXMT); /* cancel retransmit timer */
t_cancel(tp, TREXMTTL); /* cancel retransmit too long timer */
tp->cancelled = TRUE;
}
/*
* Process incoming data. Put the segments on sequencing queue in order,
* taking care of overlaps and duplicates. Data is removed from sequence
* queue by present_data when sequence is complete (no holes at top).
* Drop data that falls outside buffer quota if tight for space. Otherwise,
* process and recycle data held in tcp_input.
*/
rcv_text(tp, t)
register struct tcb *tp;
register struct th *t;
{
register i;
register struct th *p, *q;
register struct mbuf *m, *n;
struct ucb *up;
struct th *savq;
int j;
sequence last;
/* throw away any data we have already received */
if ((i = tp->rcv_nxt - t->t_seq) > 0) {
if (i < t->t_len) {
t->t_seq += i;
t->t_len -= i;
m_adj(dtom(t), i);
} else {
tp->ack_due = TRUE; /* send ack just in case */
return;
}
}
last = t_end(t); /* last seq # in incoming seg */
/* # buffers available to con */
up = tp->t_ucb;
i = (int)up->uc_rcv - (int)up->uc_rsize;
if (i < 0)
i = 0;
/* count buffers in segment */
for (m = dtom(t), j = 0; m != NULL; m = m->m_next)
if (m->m_len != 0)
j++;
/* not enough resources to process segment */
if (j > i && netcb.n_bufs < netcb.n_lowat) {
/* if segment preceeds top of seqeuncing queue, try to take
buffers from bottom of queue */
q = tp->t_rcv_next;
if (q != (struct th *)tp && SEQ_LT(tp->rcv_nxt, q->t_seq) &&
SEQ_LT(t->t_seq, q->t_seq))
for (p = tp->t_rcv_prev; i < j &&
p != (struct th *)tp;) {
savq = p->t_prev;
tcp_deq(p);
i += m_freem(dtom(p));
p = savq;
}
/* if still not enough room, drop text from end of segment */
if (j > i) {
for (m = dtom(t); i > 0 && m != NULL; i--)
m = m->m_next;
while (m != NULL) {
t->t_len -= m->m_len;
last -= m->m_len;
m->m_len = 0;
m = m->m_next;
}
tp->dropped_txt = TRUE;
if (SEQ_LT(last, t->t_seq))
return;
}
}
/* merge incoming data into the sequence queue */
q = tp->t_rcv_next; /* -> top of sequencing queue */
/* skip frags which new doesn't overlap at end */
while ((q != (struct th *)tp) && SEQ_GT(t->t_seq, t_end(q)))
q = q->t_next;
if (q == (struct th *)tp) { /* frag at end of chain */
if (SEQ_GEQ(last, tp->rcv_nxt)) {
tp->net_keep = TRUE;
tcp_enq(t, tp->t_rcv_prev);
}
} else {
/* frag doesn't overlap any on chain */
if (SEQ_LT(last, q->t_seq)) {
tp->net_keep = TRUE;
tcp_enq(t, q->t_prev);
/* new overlaps beginning of next frag only */
} else if (SEQ_LT(last, t_end(q))) {
if ((i = last - q->t_seq + 1) < t->t_len) {
t->t_len -= i;
m_adj(dtom(t), -i);
tp->net_keep = TRUE;
tcp_enq(t, q->t_prev);
}
/* new overlaps end of previous frag */
} else {
savq = q;
if (SEQ_LEQ(t->t_seq, q->t_seq)) { /* complete cover */
savq = q->t_prev;
tcp_deq(q);
m_freem(dtom(q));
} else { /* overlap */
if ((i = t_end(q) - t->t_seq + 1) < t->t_len) {
t->t_seq += i;
t->t_len -= i;
m_adj(dtom(t), i);
} else
t->t_len = 0;
}
/* new overlaps at beginning of successor frags */
q = savq->t_next;
while ((q != (struct th *)tp) && (t->t_len != 0) &&
SEQ_LT(q->t_seq, last))
/* complete cover */
if (SEQ_LEQ(t_end(q), last)) {
p = q->t_next;
tcp_deq(q);
m_freem(dtom(q));
q = p;
} else { /* overlap */
if ((i = last-q->t_seq+1) < t->t_len) {
t->t_len -= i;
m_adj(dtom(t), -i);
} else
t->t_len = 0;
break;
}
/* enqueue whatever is left of new before successors */
if (t->t_len != 0) {
tp->net_keep = TRUE;
tcp_enq(t, savq);
}
}
}
/* set to ack completed data (no gaps) */
tp->rcv_nxt = firstempty(tp);
tp->ack_due = TRUE;
/* if any room remaining in rcv buf, take any unprocessed
messages and schedule for later processing */
if ((m = tp->t_rcv_unack) != NULL &&
(i = (int)up->uc_rcv - (int)up->uc_rsize) > 0 &&
netcb.n_bufs > netcb.n_lowat)
do {
/* schedule work request */
t = mtod(m, struct th *);
j = (t->t_off << 2) + sizeof(struct ip);
m->m_off += j;
m->m_len -= j;
tp->t_rcv_unack = m->m_act;
m->m_act = (struct mbuf *)0;
netstat.t_unack++;
w_alloc(INRECV, 0, tp, t);
/* remaining buffer space */
for (n = m; n != NULL; n = n->m_next)
i--;
} while ((m = tp->t_rcv_unack) != NULL && i > 0);
}
/*
* Send a reset segment
*/
send_rst(tp, n)
register struct tcb *tp;
register struct th *n;
{
register struct ucb *up;
struct socket src, dst;
u_short port;
int temp_rst;
/* don't send a reset in response to a reset */
if (n->t_flags&T_RST || (up = tp->t_ucb) == NULL)
return;
tp->snd_rst = TRUE;
temp_rst = FALSE;
if (n->t_flags&T_ACK)
tp->snd_nxt = n->t_ackno;
/* if reset required from "wildcard" listener, take addresses and
port from incoming packet */
if (up->uc_local.s_addr == 0 || up->uc_host.s_addr == 0 ||
tp->t_fport == 0) {
src = up->uc_local;
dst = up->uc_host;
port = tp->t_fport;
up->uc_local = n->t_d;
up->uc_host = n->t_s;
tp->t_fport = n->t_src;
temp_rst = TRUE;
}
tp->syn_rcvd = FALSE;
send_pkt(tp, 0, 0, NULL);
tp->ack_due = FALSE;
tp->snd_rst = FALSE;
/* restore "wildcard" addresses */
if (temp_rst) {
up->uc_local = src;
up->uc_host = dst;
tp->t_fport = port;
tp->snd_nxt = tp->iss;
if (up->uc_route != NULL) {
h_free(up->uc_route);
up->uc_route = NULL;
}
}
}
/*
* Accept data for the user to receive. Moves data from sequenced tcp
* segments from the sequencing queue to the user's receive queue (in the
* ucb). Observes locking on receive queue.
*/
present_data(tp)
register struct tcb *tp;
{
register struct th *t;
register struct ucb *up;
register struct mbuf *m, *n, *top, *bot;
register sequence ready;
int i;
up = tp->t_ucb; /* -> ucb */
/* connection must be synced and data available for user */
if (tp->syn_acked && (t = tp->t_rcv_next) != (struct th *)tp) {
/* lock the user's receive buffer */
while (up->uc_flags & ULOCK)
sleep(&up->uc_flags, PZERO);
up->uc_flags |= ULOCK;
/* find the end of the user's receive buffer */
m = up->uc_rbuf;
if (m != NULL)
while (m->m_next != NULL)
m = m->m_next;
/* move as many mbufs as possible from tcb to user queue */
ready = firstempty(tp);
while (up->uc_rsize < up->uc_rcv && t != (struct th *)tp &&
SEQ_LT(t_end(t), ready)) {
/* count mbufs in msg chunk and free null ones */
bot = NULL;
for (n = top = dtom(t); n != NULL;) {
if (n->m_len == 0) {
if (n == top)
top = n = m_free(n);
else
bot->m_next = n = m_free(n);
} else {
bot = n;
up->uc_rsize++;
n = n->m_next;
}
}
/* chain new data to user receive buf */
if (m == NULL)
up->uc_rbuf = top;
else
m->m_next = top;
if (bot != NULL)
m = bot;
/* dequeue chunk from tcb */
tcp_deq(t);
t = t->t_next;
}
/* unlock receive queue for user */
up->uc_flags &= ~ULOCK;
wakeup(&up->uc_flags);
/* awaken reader only if any data on user rcv queue */
if (up->uc_rsize != 0)
wakeup(up);
/* let user know about foreign tcp close if no more data */
if (tp->fin_rcvd && !tp->usr_closed && rcv_empty(tp))
to_user(up, UCLOSED);
}
}